package com.bestcem.xm.txtanls.util;

import cn.hutool.core.lang.Validator;
import com.bestcem.xm.common.core.constant.Constants;
import com.bestcem.xm.txtanls.dao.*;
import com.bestcem.xm.txtanls.dao.mapper.TrainModelMapper;
import com.bestcem.xm.txtanls.entity.mongo.AnalysisProject;
import com.bestcem.xm.txtanls.entity.mongo.JoinInfo;
import com.bestcem.xm.txtanls.entity.mysql.TrainModelDo;
import com.bestcem.xm.txtanls.entity.pojo.*;
import com.bestcem.xm.txtanls.enums.AnalysisStatusEnum;
import com.bestcem.xm.txtanls.enums.IndustryCodeEnum;
import com.bestcem.xm.txtanls.enums.RecordSentiEnum;
import com.bestcem.xm.txtanls.enums.TrainStatusEnum;
import com.bestcem.xm.txtanls.grpc.client.BaseUserGrpcService;
import com.bestcem.xm.txtanls.grpc.service.param.analysis.LinkageUpdateParam;
import com.bestcem.xm.txtanls.retry.UpdateProjectStatusSpringRetry;
import com.bestcem.xm.txtanls.service.AnalysisProjectService;
import com.bestcem.xm.txtanls.service.KeywordService;
import com.bestcem.xm.txtanls.service.TopicService;
import com.bestcem.xm.txtanls.service.dto.analysis.AnalysisProjectDTO;
import com.bestcem.xm.txtanls.service.dto.train.FreeDataDTO;
import com.bestcem.xm.txtanls.service.dto.user.IndustryDTO;
import com.bestcem.xm.txtanls.service.dto.user.OrgInfoDTO;
import com.bestcem.xm.txtanls.service.dto.user.UserDTO;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.bson.types.ObjectId;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
import org.springframework.util.ObjectUtils;

import javax.annotation.Resource;
import java.lang.reflect.Field;
import java.util.*;
import java.util.stream.Collectors;

import static com.bestcem.xm.txtanls.util.ViewpointUtil.FREE_DICT_VIEWS;
import static com.bestcem.xm.txtanls.util.ViewpointUtil.FREE_VIEW_DICT;

/**
 * 文本分析项目相关公共方法
 *
 * @author XiaoYunSong <ys.xiao@idiaoyan.com>
 * @version v1.0
 * @date 2021/5/11-11:20
 */
@Slf4j
@Component
public class AnalysisUtil {
    private static final Logger EMAIL_LOG = LoggerFactory.getLogger("EmailErrorLog");

    @Resource
    private AnalysisProjectService analysisProjectService;
    @Resource
    private ViewpointDao viewPointDao;
    @Resource
    private SurveyTxtanlsFieldDao surveyTxtanlsFieldDao;
    @Resource
    private AnalysisProjectDao analysisProjectDao;
    @Resource
    private KeywordService keyWordService;
    @Resource
    private IndustryToKeywordDao industryToKeywordDao;
    @Resource
    private UpdateProjectStatusSpringRetry updateProjectStatusSpringRetry;
    @Resource
    private BaseUserGrpcService baseUserGrpcService;
    @Resource
    private TestAsyncTaskUtil testAsyncTaskUtil;
    @Resource
    private AsyncTaskUtil asyncTaskUtil;
    @Resource
    private TopicService topicService;
    @Resource
    private TopicDao topicDao;

    @Resource
    private TrainModelMapper trainModelMapper;

    /**
     * 初始化项目信息
     *
     * @param projectId 项目id
     * @param orgId     组织id
     * @return True if init success
     * @author XiaoYunSong <ys.xiao@idiaoyan.cn>
     * @date 2021/5/17 16:53
     */
    public boolean initAnalysisProject(String projectId, String orgId, String trainModelId) {
        if (log.isInfoEnabled()) {
            log.info("[Txtanls] 初始化项目信息: {}", projectId);
        }
        // 行业信息
        if (StringUtils.isNotBlank(trainModelId)) {
            TrainModelDo trainModel = trainModelMapper.getById(trainModelId);
            if (Objects.isNull(trainModel)) {
                log.error("[Txtanls] 初始化项目信息失败,项目id:{},训练模型为空,模型id:{}", projectId, trainModelId);
                return false;
            }
            // 初始化项目对应行业观点
            initViewpoint(projectId, trainModel.getCode());
        }

        // 初始化项目对应的默认话题组
        topicDao.insertGroup(projectId, "未分组");
        // 根据企业id和行业id获取行业对应行业关键词
//        List<IndustryKeywordDO> keywordEntityList = industryToKeywordDao.findOrgIndustryKeyword(orgId, industryId);
        // 未获取到行业关键词, 初始化关键词
//        if (CollectionUtils.isEmpty(keywordEntityList)) {
//            keyWordService.initIndustryKeyword(orgId, industryDTO);
//        }
        //更新训练状态和训练进度到初始状态
        analysisProjectDao.updateAnalysisProjectProgress(projectId, TrainStatusEnum.FINISHED, Constants.PROGRESS_NOT_START);
        // 更新项目状态为 CREATED, 触发异步任务
        updateProjectStatus(projectId, AnalysisStatusEnum.CREATED);
        if (log.isInfoEnabled()) {
            log.info("[Txtanls] 初始化项目信息完成: {}", projectId);
        }
        return true;
    }

    /**
     * 根据用户id获取用户名
     *
     * @param userId 用户id
     * @return User name corresponding to userId
     * @author XiaoYunSong <ys.xiao@idiaoyan.cn>
     * @date 2021/5/17 16:56
     */
    public String getUserName(String userId) {
        if (Validator.isEmpty(userId)) {
            return "默认用户名";
        }
        UserDTO userDTO = baseUserGrpcService.getUser(userId);
        if (Objects.isNull(userDTO) || ObjectUtils.isEmpty(userDTO.getUserName())) {
            log.error("[Txtanls] {} 用户信息查询失败", userId);
            return "默认用户名";
        }
        return userDTO.getUserName();
    }

    /**
     * 设置文本分析项目创建者信息
     *
     * @param dto 文本分析项目DTO
     * @return Set completed project information
     * @author XiaoYunSong <ys.xiao@idiaoyan.cn>
     * @date 2021/5/17 16:56
     */
    public AnalysisProjectDTO setUserInfo(AnalysisProjectDTO dto) {
        dto.setCreator(getUserName(dto.getUserId()));
        return dto;
    }


    /**
     * 设置文本分析项目训练状态信息
     *
     * @param dto 文本分析项目DTO
     * @return Set completed project information
     * @author XiaoYunSong <ys.xiao@idiaoyan.cn>
     * @date 2021/5/17 16:57
     */
    public AnalysisProjectDTO setProjectTrainInfo(AnalysisProjectDTO dto) {
        if (ObjectUtils.isEmpty(dto.getTrainRemainTime())) {
            dto.setTrainRemainTime("");
        }
        if (ObjectUtils.isEmpty(dto.getTrainStatus())) {
            dto.setTrainStatus(TrainStatusEnum.FINISHED.getIndex());
        }
        return dto;
    }

    /**
     * 问卷训练项目将字段信息中的开放选项id替换为开放选项标题
     *
     * @param dto 问卷训练项目信息
     * @return Replace completed project information
     * @author XiaoYunSong <ys.xiao@idiaoyan.cn>
     * @date 2021/5/17 11:33
     */
    public AnalysisProjectDTO setSurveyProjectFiledInfo(AnalysisProjectDTO dto) {
        List<SurveyTxtanlsFieldDO> fieldEntityList = surveyTxtanlsFieldDao.findProjectAllTextFiled(dto.getId());
        Map<String, SurveyTxtanlsFieldDO> fieldEntityMap =
                fieldEntityList.stream().collect(Collectors.toMap(SurveyTxtanlsFieldDO::getId, self -> self, (prev, now) -> prev));
        for (AnalysisProject.TextField textField : dto.getTextFieldList()) {
            SurveyTxtanlsFieldDO entity = fieldEntityMap.get(textField.getId());
            if (Objects.nonNull(entity)) {
                textField.setValue(entity.getSurveyColName());
            }
        }
        return dto;
    }

    /**
     * 项目更新后进行重新训练
     *
     * @param entity 更新后的项目
     * @author XiaoYunSong <ys.xiao@idiaoyan.cn>
     * @date 2021/5/17 16:58
     */
    public void retrainProject(ProjectDO entity) {
        // 更新项目状态为 CREATED, 触发异步任务
        updateProjectStatus(entity.getId(), AnalysisStatusEnum.CREATED);
    }

    /**
     * 初始化项目行业观点
     *
     * @param projectId      项目id
     * @param trainModelCode 行业code
     * @author XiaoYunSong <ys.xiao@idiaoyan.cn>
     * @date 2021/5/13 14:09
     */
    private void initViewpoint(String projectId, Integer trainModelCode) {
        if (log.isInfoEnabled()) {
            log.info("[Txtanls] 项目 {} 行业观点初始化开始", projectId);
        }
        Map<String, Map<String, List<String>>> initViewPointMap = ViewpointUtil.TRADE_DICT.get(IndustryCodeEnum.getByCode(trainModelCode));
        if (Objects.isNull(initViewPointMap)) {
            if (log.isInfoEnabled()) {
                log.info("[Txtanls] 项目 {} 无对应行业观点", projectId);
            }
            return;
        }
        initViewPointMap.forEach((fatherPointContent, fatherPointMap) -> {
            String pId = viewPointDao.insertParentViewpoint(fatherPointContent, projectId, null);
            List<String> rows = fatherPointMap.get(ViewpointUtil.ROW);
            List<String> zhs = fatherPointMap.get(ViewpointUtil.ZH);
            if (Objects.isNull(rows) || Objects.isNull(zhs) || rows.size() != zhs.size()) {
                log.error("[Txtanls] 项目: {} 初始化行业观点数据错误", projectId);
                EMAIL_LOG.error("[Txtanls] 项目: {} 初始化行业观点数据错误", projectId);
                return;
            }
            int len = rows.size();
            for (int i = 0; i < len; i++) {
                String row = rows.get(i), zh = zhs.get(i);
                viewPointDao.insertChildrenViewpoint(zh, row, projectId, pId, null);
            }
        });
        if (log.isInfoEnabled()) {
            log.info("[Txtanls] 项目 {} 行业观点初始化完成", projectId);
        }
    }

    /**
     * 更新项目状态
     *
     * @param projectId 项目id
     * @param status    目标状态
     * @author XiaoYunSong <ys.xiao@idiaoyan.cn>
     * @date 2021/6/2 15:31
     */
    public int updateProjectStatus(String projectId, AnalysisStatusEnum status) {
        //训练之后更新项目状态
        int updateResult = updateProjectStatusSpringRetry.updateProjectStatus(projectId, status);
        if (updateResult == 0) {
            log.error("[Txtanls] 文本分析项目状态更新失败; ProjectId: {}; Status: {};", projectId, status);
        }
        return updateResult;
    }

    /**
     * 获取组织所属行业信息
     *
     * @param orgId 组织id
     * @return Organization's industry information
     * @author XiaoYunSong <ys.xiao@idiaoyan.cn>
     * @date 2021/7/15 17:26
     */
    public IndustryDTO getIndustryInfo(String orgId) {
        OrgInfoDTO orgInfoDTO = baseUserGrpcService.getOrganization(orgId);
        if (Objects.isNull(orgInfoDTO)) {
            log.error("[Txtanls] {} 组织信息查询失败", orgId);
            return null;
        }
        // 获取创建者组织所在行业id
        String industryId = orgInfoDTO.getIndustryID();
        return baseUserGrpcService.getIndustry(industryId);
    }

    /**
     * 回滚创建成功但后续操作失败的项目
     *
     * @param projectId 项目id
     * @author XiaoYunSong <ys.xiao@idiaoyan.cn>
     * @date 2021/7/15 17:26
     */
    public void rollbackProject(String projectId) {
        log.warn("[Txtanls] 项目 {} 行业数据回滚", projectId);
        analysisProjectService.deleteProject(projectId);
    }

    public int updateFieldByColumn(LinkageUpdateParam param) {
        // 更新的是时间字段
        if (param.getTime()) {
            return analysisProjectDao.updateTimeFieldByTimeId(param.getId(), param.getName());
        }

        // 更新的是文本字段
        // 查找所有用了对应文本字段的项目
        List<ProjectDO> projectList = analysisProjectDao.findAllProjectByFieldId(param.getId());
        // 更新项目
        int count = 0;
        for (ProjectDO project : projectList) {
            List<ProjectFieldDO> textFieldList = project.getTextFieldList();
            for (ProjectFieldDO textField : textFieldList) {
                if (param.getId().equals(textField.getId())) {
                    textField.setValue(param.getName());
                }
            }
            analysisProjectDao.updateProjectFieldList(project.getId(), textFieldList);
            count += 1;
        }
        return count;
    }

    /**
     * 为用户创建免费项目
     *
     * @param orgId o
     * @author gy.liu
     * @date 2021/7/16
     */
    public void initAnalysisProjectForUse(String orgId) {
        try {
            if (log.isInfoEnabled()) {
                log.info("[Txtanls] 组织 {} 初始化文本分析示例项目", orgId);
            }

            List<UserDTO> userDTOList = baseUserGrpcService.listUserByOrgId(orgId);
            if (CollectionUtils.isEmpty(userDTOList)) {
                log.error("[Txtanls] 组织 {} 下无用户", orgId);
                return;
            }
            String userId = userDTOList.get(0).getId();
            //初始化项目
            ProjectDO analysisProject = new ProjectDO();
            analysisProject.setName("文本分析项目示例");
            analysisProject.setCreator(userId);
            analysisProject.setCreatedId(userId);
            analysisProject.setUpdatedId(userId);
            analysisProject.setUserId(userId);
            analysisProject.setStatus(AnalysisStatusEnum.FINISHED.getIndex());
            analysisProject.setOrgId(orgId);
            analysisProject.setTableId(new ObjectId().toString());
            analysisProject.setTableName("餐饮数据源demo");

            List<ProjectFieldDO> textFields = new ArrayList<>();
            ProjectFieldDO textField = new ProjectFieldDO();
            textField.setId(new ObjectId().toString());
            textField.setValue("texts");
            textFields.add(textField);
            analysisProject.setTextFieldList(textFields);

            analysisProject.setTimeFieldId(new ObjectId().toString());
            analysisProject.setTimeFieldName("times_");
            analysisProject.setTrainStatus(TrainStatusEnum.FINISHED.getIndex());
            analysisProject.setTrainProgress(Constants.PROGRESS_FINISHED);

            ProjectDO analysisProjectNew = analysisProjectDao.insertProject(analysisProject);
            //初始化话题
            topicService.initTopic(analysisProjectNew.getId());

            IndustryDTO industryDTO = baseUserGrpcService.getIndustryByCode(IndustryCodeEnum.DIET.getIndex());
            String industryId = industryDTO.getId();
            // 初始化项目对应行业观点
            initView(analysisProjectNew.getId(), industryId);
            // 根据企业id和行业id获取行业对应行业关键词
            List<IndustryKeywordDO> keywordEntityList = industryToKeywordDao.findOrgIndustryKeyword(orgId, industryId);
            // 未获取到行业关键词, 初始化关键词
            if (CollectionUtils.isEmpty(keywordEntityList)) {
                keyWordService.initIndustryKeyword(orgId, industryDTO);
            }
            //判断行业类型,返回行业相关的观点id
            List<ViewpointDO> viewpoints = viewPointDao.findAllChildrenViewPoint(analysisProjectNew.getId());
            //获取情感接口数据并且转换数据
            Map<String, Object> dataMap = getSentimentConvert();
            if (Objects.nonNull(dataMap)) {
                List<Integer> lineList = (List<Integer>) dataMap.get("lineList");
                List<String> dataTimeList = (List<String>) dataMap.get("datetime");
                List<String> textList = (List<String>) dataMap.get("text");
                List<Map<String, String>> freeDataList = (List<Map<String, String>>) dataMap.get("data");
                List<Integer> sentiTextList = (List<Integer>) dataMap.get("sentiList");
                for (int i = 0; i < freeDataList.size(); i++) {
                    Map<String, String> sentiView = freeDataList.get(i);
                    String time = dataTimeList.get(i);
                    Integer collectionId = lineList.get(i);
                    String text = textList.get(i);
                    Integer sentiValue = sentiTextList.get(i);
                    List<JoinInfo.Viewpoint> listViewPoint = new ArrayList<>();
                    for (String senti : sentiView.keySet()) {
                        for (ViewpointDO child : viewpoints) {
                            if (!child.getViewContent().equals(FREE_DICT_VIEWS.get(senti))) {
                                continue;
                            }
                            if (RecordSentiEnum.NONE.getIndex() == Integer.parseInt(sentiView.get(senti))) {
                                continue;
                            }
                            JoinInfo.Viewpoint viewPoint = new JoinInfo.Viewpoint();
                            viewPoint.setKey(FREE_DICT_VIEWS.get(senti));
                            viewPoint.setViewId(child.getId());
                            viewPoint.setValues(Integer.valueOf(sentiView.get(senti)));
                            viewPoint.setContent(child.getViewContent());
                            listViewPoint.add(viewPoint);
                        }
                    }

                    // 保存原话记录. 没有时间格式就不保存
                    if (Validator.isNotEmpty(time)) {
                        RecordDO recordDOEntity = asyncTaskUtil.saveRecordNew(analysisProjectNew, time, collectionId + "", text, sentiValue, textField, industryId);
                        // 保存训练结果
                        asyncTaskUtil.saveJoinInfoNew(analysisProjectNew.getId(), time, collectionId + "", sentiValue, recordDOEntity.getId(), listViewPoint);
                    }
                }
            }
            if (log.isInfoEnabled()) {
                log.info("[Txtanls] 初始化项目信息完成: {}", analysisProjectNew.getId());
            }
        } catch (Exception e) {
            log.error("用户调用初始化项目异常:" + e);
        }
    }


    /**
     * 情感分析接口返回数据转换
     *
     * @return Map<String, Object>
     * @author gy.liu
     * @date 2021/7/19
     */
    public Map<String, Object> getSentimentConvert() {
        Map<String, Object> dataMap = new HashMap<>();
        List<Map<String, String>> freeDataList = new ArrayList<>();
        List<String> textList = new ArrayList<>();
        List<Integer> sentiList = new ArrayList<>();
        List<String> dataTimeList = new ArrayList<>();
        List<Integer> lineList = new ArrayList<>();
        //读取初始化情感结果
        List<FreeDataDTO> demoDataDTOS = testAsyncTaskUtil.readFreeDataExcel();
        if (demoDataDTOS.size() > 0) {
            int i = 0;
            for (FreeDataDTO freeDataDTO : demoDataDTOS) {
                int positive = 0;
                int negative = 0;
                int neutral = 0;
                int none = 0;
                lineList.add(i);
                Field[] fields = freeDataDTO.getClass().getDeclaredFields();
                if (fields.length > 0) {
                    Map<String, String> map = new Hashtable<>();
                    for (Field field : fields) {
                        field.setAccessible(true);
                        try {
                            String value = (String) field.get(freeDataDTO);
                            String name = field.getName();
                            if ("text".equals(name)) {
                                textList.add(value);
                            }
                            if ("datetime".equals(name)) {
                                dataTimeList.add(value);
                            }
                            map.put(name, value);
                            if (!"text".equals(name) && !"datetime".equals(name)) {
                                int valueI = Integer.parseInt(value);
                                if (RecordSentiEnum.POSITIVE.getIndex() == valueI) {
                                    positive++;
                                } else if (RecordSentiEnum.NEGATIVE.getIndex() == valueI) {
                                    negative++;
                                } else if (RecordSentiEnum.NEUTRAL.getIndex() == valueI) {
                                    neutral++;
                                } else if (RecordSentiEnum.NONE.getIndex() == valueI) {
                                    none++;
                                }
                            }
                        } catch (IllegalAccessException e) {
                            log.error("不合法数据:" + e);
                        }
                    }
                    if (positive > 0 && negative > 0) {
                        sentiList.add(3);
                    } else if (positive > 0 && negative == 0) {
                        sentiList.add(0);
                    } else if (positive == 0 && negative > 0) {
                        sentiList.add(1);
                    } else {
                        sentiList.add(2);
                    }
                    freeDataList.add(map);
                }
            }
            dataMap.put("text", textList);
            dataMap.put("datetime", dataTimeList);
            dataMap.put("data", freeDataList);
            dataMap.put("lineList", lineList);
            dataMap.put("sentiList", sentiList);
        }
        return dataMap;
    }

    public void initView(String projectId, String industryId) {
        List<String> views = Arrays.asList("位置", "服务", "价格", "环境", "菜品", "体验");
        for (String view : views) {
            String pId = viewPointDao.insertParentViewpoint(view, projectId, industryId);
            Map<String, List<String>> viewDicts = FREE_VIEW_DICT.get(view);
            List<String> rowList = viewDicts.get("row");
            List<String> zhList = viewDicts.get("zh");
            for (int i = 0; i < rowList.size(); i++) {
                viewPointDao.insertChildrenViewpoint(zhList.get(i), rowList.get(i), projectId, pId, industryId);
            }
        }
    }

    public String getIndustryCode(String modelId) {
        if (StringUtils.isBlank(modelId)) {
            return IndustryCodeEnum.DIET.getSpell();
        }
        TrainModelDo trainModel = trainModelMapper.getById(modelId);
        if (Objects.isNull(trainModel)) {
            return IndustryCodeEnum.DIET.getSpell();
        }
        return trainModel.getBusiness();

    }


}
